﻿package game.socket {
	import flash.net.Socket;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.ProgressEvent;
	import flash.utils.ByteArray;
	import game.debug.Debug;
	
	public class SocketConnection extends Socket {
		
		public static const DISCONNECT:String		= "disconnect";
		public static const CONNECT:String			= "connect";
		public static const CONNECTING:String		= "connecting";
		
		public var data:SocketObject;
		public var status:String;
		public var packages:Vector.<ByteArray> = new Vector.<ByteArray>;
		
		private var sendIndex:int = 0;
		
		public function SocketConnection(data:SocketObject):void {
			
			this.data	= data;
			this.status	= SocketConnection.DISCONNECT;
			this.endian	= SocketConfig.ENDIAN;
			this.timeout= SocketConfig.TIMEOUT*1000;
			
			this.addEventListener (Event.CLOSE,							closeHandler);
			this.addEventListener (Event.CONNECT,						connectHandler);
			this.addEventListener (IOErrorEvent.IO_ERROR,				ioErrorHandler);
			this.addEventListener (SecurityErrorEvent.SECURITY_ERROR,	securityErrorHandler);
			this.addEventListener (ProgressEvent.SOCKET_DATA,			dataHandler);
			
		}
		
		private function dataHandler(event:ProgressEvent):void {
			if (this.bytesAvailable == 0) return;
			var bytes:ByteArray = new ByteArray;
			bytes.endian = SocketConfig.ENDIAN;
			this.readBytes(bytes);
			parseBytes(bytes);
		}
		
		private function parseBytes ( bytes:ByteArray ):void {
			var id:int			= bytes.readShort();
			var length:int		= bytes.readShort();
			var read:int		= 4;
			
			if (length-read>=0 && bytes.bytesAvailable>=length-read) {
				try {
				
					var ba:ByteArray	= new ByteArray;
					ba.endian			= SocketConfig.ENDIAN;
					bytes.readBytes	(ba, 0, length-read);
					
					var CLASS:Class = getClassByID(id);
					if (CLASS) {
						this.dispatchEvent( new SocketHandler ( SocketHandler.RECIVE, new CLASS(ba) ) );
					} else {
						Debug.log ('Неизвестный пакет: ', Debug.SOCKET);
					}
					
				} catch (e:Error) {
					Debug.log (String(e.message), Debug.SOCKET);
				}
			}
			
			if (bytes.bytesAvailable>0) 
				parseBytes(bytes);
		}
		
		public function getClassByID(id:int):Class {
			var CLASS:Class;
			for each (var c:Class in SocketConfig.CLASSES) {
				if (c.ID == id) {
					CLASS = c; 
					break;
				}
			}
			return CLASS;
		}
		
		public function send(ba:ByteArray):void {
			packages.push(ba);
			
			if (status==SocketConnection.CONNECT) sendAllPackages();
			else connecting();
		}
		
		private function sendAllPackages():void {
			while (sendIndex<packages.length) {
				this.writeBytes(packages[sendIndex]);
				sendIndex++;
			}
			
			this.flush();
		}
		
		public function connecting():void {
			status = SocketConnection.CONNECTING;
			
			connect(data.host, data.port);
		}
		
		public override function close():void {
			try {
				super.close();
			} catch (e:Error) {
				Debug.log (String(e.message), Debug.SOCKET);
			}
			
			this.status	= SocketConnection.DISCONNECT;
		}
		
		private function connectHandler(event:Event):void {
			this.dispatchEvent( new SocketEvent ( SocketEvent.CONNECTED, this ) );
				
			this.status	= SocketConnection.CONNECT;
			sendAllPackages();
		}
		
		private function closeHandler(event:Event):void {
			this.status	= SocketConnection.DISCONNECT;
			
			this.dispatchEvent( new SocketEvent ( SocketEvent.DISCONNECTED, this ) );
		}
		
		private function ioErrorHandler(event:IOErrorEvent):void {
			this.status	= SocketConnection.DISCONNECT;
			
			this.dispatchEvent( new SocketEvent ( SocketEvent.ERROR, this ) );
			this.dispatchEvent( new SocketEvent ( SocketEvent.DISCONNECTED, this ) );
		}
		
		private function securityErrorHandler(event:SecurityErrorEvent):void {
			this.status	= SocketConnection.DISCONNECT;
			
			//this.dispatchEvent( new SocketEvent ( SocketEvent.ERROR, this ) );
			this.dispatchEvent( new SocketEvent ( SocketEvent.DISCONNECTED, this ) );
		}
		
	}
	
}